%%% -------------------------------------------------------------------
%%% Author  : vivimice
%%% Description :
%%%
%%% Created : Feb 16, 2012
%%% -------------------------------------------------------------------
-module(daemon).

-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------

-define(TCP_OPTS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, false}]).

%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([start/0, start/1, stop/0]).

%% --------------------------------------------------------------------
%% Internal exports
%% --------------------------------------------------------------------
-export([loop_accept/1]).

-record(state, {listen_socket, eredis_client}).

%% ====================================================================
%% External functions
%% ====================================================================
start() ->
	start(23).

start(Port) ->
	gen_server:start_link({local, daemon}, ?MODULE, [Port], []).

stop() ->
	gen_server:cast(daemon, stop).

%% ====================================================================
%% Server functions
%% ====================================================================

%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%% --------------------------------------------------------------------
init([Port]) ->
	% start listening on port
	case gen_tcp:listen(Port, ?TCP_OPTS) of
		{ok, ListenSocket} ->
			
			% start eredis client
			case eredis:start_link("192.168.148.101", 6379) of
				
				{ok, EredisClient} ->
					% start services
					service_supervisor:start_link(EredisClient),
					
					% start accepting incoming connections
					spawn_link(?MODULE, loop_accept, [ListenSocket]),
					
					% init finished
					logger:info("Daemon started."),
    				{ok, #state{listen_socket = ListenSocket, eredis_client = EredisClient}};
				
				{error, Reason} ->
					logger:error("Failed connecting to db, Reason: ~p", [Reason]),
					gen_tcp:close(ListenSocket),
					{stop, {db_error, Reason}}
			
			end;
		
		{error, Reason} ->
			logger:error("Failed listening on port ~p, Reason: ~p", [Port, Reason]),
			{stop, {socket_error, Reason}}
	end.

%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call(Request, _From, State) ->
    logger:warn("Unimplemented handle_call for request: ~w", [Request]),
    {reply, unimpl, State}.

%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(stop, State) ->
	{stop, normal, State};

handle_cast(Msg, State) ->
	logger:warn("Unimplemented handle_cast for message: ~w", [Msg]),
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(Reason, State) ->
	gen_tcp:close(State#state.listen_socket),
	eredis:stop(State#state.eredis_client),
	logger:info("Daemon stopped, caused by: ~w", [Reason]),
    ok.

%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------
loop_accept(ListenSocket) ->
	case gen_tcp:accept(ListenSocket) of
		{ok, Socket} ->
			worker:create(Socket),
			?MODULE:loop_accept(ListenSocket);
		{error, closed} ->
			logger:warn("Listening socket closed. Acceptor quit.");
		{error, Reason} ->
			logger:error("Error accepting incoming connection: ~p", [Reason])
	end.
